﻿using DotXxlJob.Core.Config;
using DotXxlJob.Core.Extensions;
using DotXxlJob.Core.Model;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Nest;
using Newtonsoft.Json.Linq;
using System.Diagnostics;
using System.Text;
using XYS.Core.Models;
using XYS.Core.Util;
using XYS.Service.Core.Util.Helper;

namespace XYS.Service.Core.Service.XXLJobServices
{
    /// <summary>
    /// 
    /// </summary>
    public class JobLoger : DotXxlJob.Core.JobLogger, IJobLogger
    {
        private readonly IElasticClient esClient;
        private readonly ILogger<DotXxlJob.Core.JobLogger> logger;
        private readonly IOptions<XxlJobExecutorOptions> optionsAccessor;
        /// <summary>
        /// 
        /// </summary>
        /// <param name="optionsAccessor"></param>
        /// <param name="logger"></param>
        public JobLoger(IOptions<XxlJobExecutorOptions> optionsAccessor, ILogger<DotXxlJob.Core.JobLogger> logger) : base(optionsAccessor, logger)
        {
            this.optionsAccessor = optionsAccessor;
            this.logger = logger;
            esClient = ElasticClientHelper.GetClient(GlobalCoreSetting.MQSetting.ElasticName);
        }

        private readonly AsyncLocal<long> LogId = new AsyncLocal<long>();

        public override void SetLogFile(long logTime, long logId)
        {
            base.SetLogFile(logTime, logId);
            LogId.Value = logId;
        }
        /// <summary>
        /// 写日志
        /// </summary>
        /// <param name="pattern"></param>
        /// <param name="format"></param>
        public override void Log(string pattern, params object[] format)
        {
            base.Log(pattern, format);
            if (esClient == null || !optionsAccessor.Value.EnableRemoteLog) return;
            Task.Run(() =>
            {
                string context = format.IsNullOrEmpty() ? pattern : string.Format(pattern, format);
                //保存记录至ES
                string indexName = $"xxljob-log-{GlobalCoreSetting.ServiceName}-{GlobalCoreSetting.EnvironmentName}-{DateTime.Now:yyyy-MM-dd}".ToLower();
                var resultBulkES = esClient.Bulk(indexName, BuildLogContext(context, LogId.Value));
            });
        }

        /// <summary>
        /// 构建日志内容
        /// </summary>
        /// <param name="context"></param>
        /// <param name="logId"></param>
        /// <param name="isSpecial"></param>
        /// <returns></returns>
        private object BuildLogContext(string context, long logId, bool isSpecial = false)
        {
            var callInfo = new StackTrace(true).GetFrame(1);
            return new
            {
                ServiceName = GlobalCoreSetting.ServiceName,
                EnvironmentName = GlobalCoreSetting.EnvironmentName,
                Context = base.BuildLogContent(callInfo, context),
                IsSpecial = isSpecial,
                LogId = logId,

                AdminAddresses = optionsAccessor.Value.AdminAddresses,
                AppName = optionsAccessor.Value.AppName,
                SpecialBindUrl = optionsAccessor.Value.SpecialBindUrl,
                SpecialBindAddress = optionsAccessor.Value.SpecialBindAddress,
                Port = optionsAccessor.Value.Port,
                AutoRegistry = optionsAccessor.Value.AutoRegistry,
                AccessToken = optionsAccessor.Value.AccessToken,

                CreateTime = DateTime.Now
            };
        }

        /// <summary>
        /// 写异常日志
        /// </summary>
        /// <param name="ex"></param>
        public override void LogError(Exception ex)
        {
            Log($"{ex.Message}\r\n{ex.StackTrace}");
        }

        /// <summary>
        /// 写特殊日志
        /// </summary>
        /// <param name="logTime"></param>
        /// <param name="logId"></param>
        /// <param name="pattern"></param>
        /// <param name="format"></param>
        public override void LogSpecialFile(long logTime, long logId, string pattern, params object[] format)
        {
            base.LogSpecialFile(logTime, logId, pattern, format);
            if (esClient == null || !optionsAccessor.Value.EnableRemoteLog) return;
            Task.Run(() =>
            {
                string context = format.IsNullOrEmpty() ? pattern : string.Format(pattern, format);

                //保存记录至ES
                string indexName = $"xxljob-log-{GlobalCoreSetting.ServiceName}-{GlobalCoreSetting.EnvironmentName}-{logTime.FromMilliseconds():yyyy-MM-dd}".ToLower();
                var resultBulkES = esClient.Bulk(indexName, BuildLogContext(context, logId, true));
            });
        }

        /// <summary>
        /// 读取日志
        /// </summary>
        /// <param name="logTime"></param>
        /// <param name="logId"></param>
        /// <param name="fromLine"></param>
        /// <returns></returns>
        public override LogResult ReadLog(long logTime, long logId, int fromLine)
        {
            var result = base.ReadLog(logTime, logId, fromLine);
            if (result.ToLineNum > 0 || esClient == null || !optionsAccessor.Value.EnableRemoteLog) return result;

            string indexName = $"xxljob-log-{GlobalCoreSetting.ServiceName}-{GlobalCoreSetting.EnvironmentName}-{logTime.FromMilliseconds():yyyy-MM-dd}".ToLower();

            var list = esClient.Search<object>(searchDescriptor =>
            {
                return searchDescriptor
                .Index(Indices.Parse(indexName))
                .From(0).Size(10000)
                .Sort(sort => sort.Field("createTime", SortOrder.Ascending))
                .Query(queryDescriptor =>
                {
                    return queryDescriptor.Bool(boolDescriptor =>
                    boolDescriptor.Must(
                        matchDescriptor => matchDescriptor.Match(matchDescriptor => matchDescriptor.Field(new Field("serviceName")).Query(GlobalCoreSetting.ServiceName)),
                        matchDescriptor => matchDescriptor.Match(matchDescriptor => matchDescriptor.Field(new Field("environmentName")).Query(GlobalCoreSetting.EnvironmentName)),
                        matchDescriptor => matchDescriptor.Match(matchDescriptor => matchDescriptor.Field(new Field("appName")).Query(optionsAccessor.Value.AppName)),
                        //matchDescriptor => matchDescriptor.Match(matchDescriptor => matchDescriptor.Field(new Field("specialBindAddress")).Query(optionsAccessor.Value.SpecialBindAddress)),
                        //matchDescriptor => matchDescriptor.Match(matchDescriptor => matchDescriptor.Field(new Field("port")).Query(optionsAccessor.Value.Port.ToString())),
                        matchDescriptor => matchDescriptor.Match(matchDescriptor => matchDescriptor.Field(new Field("logId")).Query(logId.ToString()))
                    ));
                });
            });
            if (list.Total == 0) return result;

            StringBuilder sb = new StringBuilder();
            foreach (var item in list.Hits)
            {
                JToken jtoken = JToken.Parse(item.Source.ToJson());
                string text = jtoken.GetValue<string>("context") ?? "";
                sb.Append(text);
            }
            //获取本地日志文件
            var filePath = base.MakeLogFileName(logTime, logId);
            //获取目录
            var directory = Path.GetDirectoryName(filePath);
            //如果目录不存在，则创建
            if (!Directory.Exists(directory)) Directory.CreateDirectory(directory);
            File.WriteAllText(filePath, sb.ToString());
            return base.ReadLog(logTime, logId, fromLine);
        }

    }
}
